home *** CD-ROM | disk | FTP | other *** search
/ JCSM Shareware Collection 1996 September / JCSM Shareware Collection (JCS Distribution) (September 1996).ISO / prgtools / euphor13.zip / ED.EX < prev    next >
Text File  |  1995-05-13  |  47KB  |  1,862 lines

  1.     ----------------------------------------------------------
  2.     --       This Euphoria Editor was developed by          --
  3.     --            Rapid Deployment Software.                --
  4.     --                                                      --
  5.     -- Permission is freely granted to anyone to modify     --
  6.     -- and/or redistribute this editor (ed.ex, syncolor.e). --
  7.     -- You may even sell it as it is, or with your          --
  8.     -- modifications.                                       --
  9.     ----------------------------------------------------------
  10.  
  11. without type_check -- makes it a bit faster
  12.  
  13. include graphics.e
  14. include get.e
  15. include file.e
  16.  
  17. constant TRUE = 1,
  18.      FALSE = 0
  19.  
  20. -------- user-modifiable parameters: 
  21.  
  22. constant PROG_INDENT = 4  -- tab width for editing program source files
  23.               -- (tab width is 8 for other files)
  24. constant E_FILES = {".e", ".ex", ".pro"}             -- Euphoria files
  25. constant PROG_FILES = E_FILES & {".c", ".h", ".bas"} -- program indent files
  26.  
  27. constant WANT_COLOR_SYNTAX  = TRUE -- FALSE if you don't want
  28.                    -- color syntax highlighting
  29.  
  30. constant WANT_AUTO_COMPLETE = TRUE -- FALSE if you don't want 
  31.                    -- auto-completion of Euphoria statements
  32.  
  33. constant HOT_KEYS = TRUE  -- FALSE if you want to hit Enter after each 
  34.               -- command like you did with v1.2 
  35.  
  36. -- cursor style: 
  37. constant ED_CURSOR = UNDERLINE_CURSOR 
  38.           -- BLOCK_CURSOR
  39.           -- HALF_BLOCK_CURSOR
  40.            
  41. -- number of lines on screen: (25,28,43,50)
  42. constant INITIAL_LINES = 25,  -- when editor starts up
  43.      FINAL_LINES = 25     -- when editor is finished
  44.  
  45. -- colors
  46. constant TOP_LINE_TEXT_COLOR = BLACK,
  47.      TOP_LINE_BACK_COLOR = BROWN,
  48.      TOP_LINE_DIM_COLOR = GRAY,
  49.      BACKGROUND_COLOR = WHITE
  50.  
  51. -- colors needed by syncolor.e:
  52. -- Adjust to suit your monitor and your taste.
  53. global constant NORMAL_COLOR = GRAY,   -- BLACK might look better
  54.         COMMENT_COLOR = RED,
  55.         KEYWORD_COLOR = BLUE,
  56.         BUILTIN_COLOR = MAGENTA,
  57.         STRING_COLOR = BROWN,  -- GREEN might look better
  58.         BRACKET_COLOR = {NORMAL_COLOR, BLACK, YELLOW, GREEN, 
  59.                  BRIGHT_WHITE, CYAN, BRIGHT_BLUE}
  60.  
  61. constant CONTROL_CHAR = 254 -- change funny control chars to this
  62.  
  63. -------- end of user-modifiable parameters 
  64.  
  65.  
  66. -- output device:
  67. global constant SCREEN = 1
  68.  
  69. constant SCREEN_WIDTH = 80
  70.  
  71. global constant BLANK_LINE = repeat(' ', SCREEN_WIDTH)
  72.  
  73. -- special input characters
  74. constant ESCAPE = 27,
  75.      CR = 13,
  76.      BS = 8,
  77.      HOME = 327,
  78.      PAGE_UP = 329,
  79.      END = 335,
  80.      PAGE_DOWN = 337,
  81.      INSERT = 338,
  82.      DELETE = 339,
  83.      ARROW_LEFT = 331,
  84.      ARROW_RIGHT = 333,
  85.      ARROW_UP = 328,
  86.      ARROW_DOWN = 336,
  87.      F1 = 315,
  88.      F10 = 324,
  89.      CONTROL_DELETE = 403, -- key for line-delete 
  90.                    -- (not available on some systems)
  91.      CONTROL_D      = 4    -- alternate key for line-delete  
  92.  
  93. constant CONTROL_CHARS = {ESCAPE, BS, DELETE, PAGE_UP, PAGE_DOWN,
  94.               INSERT, CONTROL_DELETE, CONTROL_D,
  95.               ARROW_LEFT, ARROW_RIGHT, ARROW_UP, ARROW_DOWN,
  96.               HOME, END, F1, F1+1, F1+2, F1+3, F1+4, F1+5,
  97.               F1+6, F1+7, F1+8, F10}
  98.  
  99. constant STANDARD_TAB_WIDTH = 8
  100.  
  101. constant MAX_WINDOWS = 10 -- F1..F10
  102.  
  103. type boolean(integer x)
  104.     return x = TRUE or x = FALSE
  105. end type
  106.  
  107. type natural(integer x)
  108.     return x >= 0
  109. end type
  110.  
  111. type positive_int(integer x)
  112.     return x >= 1
  113. end type
  114.  
  115. sequence buffer -- In-memory buffer where the file is manipulated.
  116. -- This is a sequence where each element is a sequence
  117. -- containing one line of text. Each line of text ends with '\n'
  118.  
  119. positive_int screen_length  -- number of lines on physical screen
  120.  
  121. positive_int window_base    -- location of first line of current window 
  122.                 -- (status line)
  123. window_base = 1
  124. positive_int window_length  -- number of lines of text in current window
  125.  
  126. sequence window_list -- state info for all windows
  127. window_list = {0}
  128.  
  129. sequence buffer_list -- all buffers
  130. buffer_list = {}
  131.  
  132. type window_id(integer x)
  133.     return x >= 1 and x <= length(window_list)
  134. end type
  135.  
  136. type buffer_id(integer x)
  137.     return x >= 0 and x <= length(buffer_list)
  138. end type
  139.  
  140. type window_line(integer x)
  141. -- a valid line in the current window
  142.     return x >= 1 and x <= window_length
  143. end type
  144.  
  145. type screen_col(integer x)
  146. -- a valid column on the screen
  147.     return x >= 1 and x <= SCREEN_WIDTH
  148. end type
  149.  
  150. type buffer_line(integer x)
  151. -- a valid buffer line
  152.     return (x >= 1 and x <= length(buffer)) or x = 1
  153. end type
  154.  
  155. type char(integer x)
  156. -- a character
  157.     return x >= 0 and x <= 511
  158. end type
  159.  
  160. type extended_char(integer x)
  161.     return char(x) or x = -1
  162. end type
  163.  
  164. type file_number(integer x)
  165.     return x >= -1
  166. end type
  167.  
  168. sequence file_name   -- name of the file that we are editing
  169.  
  170. -- These are the critical state variables that all editing operations
  171. -- must update:
  172. buffer_line  b_line  -- current line in buffer
  173. positive_int b_col   -- current character within line in buffer
  174. window_line  s_line  -- line on screen corresponding to b_line
  175. screen_col   s_col   -- column on screen corresponding to b_col
  176.  
  177. boolean stop         -- indicates when to stop processing current buffer
  178.  
  179. sequence kill_buffer -- kill buffer of deleted lines or characters
  180. kill_buffer = {}
  181.  
  182. boolean adding_to_kill -- TRUE if still accumulating deleted lines/chars
  183.  
  184. boolean multi_color   -- use colors for keywords etc.
  185. boolean auto_complete -- perform auto completion of statements
  186. boolean dot_e         -- TRUE if this is a .e/.ex file
  187. boolean modified      -- TRUE if file has been modified
  188.              
  189. atom buffer_version,    -- version of buffer contents
  190.      my_buffer_version  -- last version used by current window
  191. boolean control_chars    -- binary file - view but don't save
  192.  
  193. natural start_line, start_col
  194.  
  195. sequence error_message
  196.  
  197. sequence config -- video configuration
  198.  
  199. window_id window_number -- current active window
  200. window_number = 1
  201.  
  202. buffer_id buffer_number -- current active buffer
  203. buffer_number = 0
  204.  
  205. procedure delay(atom n)
  206. -- an n second pause while a message is on the screen
  207.     atom t
  208.  
  209.     t = time()
  210.     while time() < t + n do
  211.     end while
  212. end procedure
  213.  
  214. procedure set_modified()
  215. -- buffer differs from file
  216.     modified = TRUE
  217.     cursor(NO_CURSOR) -- hide cursor while we update the screen
  218.     buffer_version = buffer_version + 1
  219. end procedure
  220.  
  221. procedure clear_modified()
  222. -- buffer is now same as file
  223.     modified = FALSE
  224. end procedure
  225.  
  226. function key_gets(sequence hot_keys)
  227. -- return input string from keyboard
  228. -- a bit more user-friendly than gets(0)
  229.    sequence input_string
  230.    integer line, init_column, column, char
  231.    sequence cursor
  232.    
  233.    if not HOT_KEYS then
  234.     hot_keys = ""
  235.    end if
  236.    cursor = get_position()
  237.    line = cursor[1]
  238.    init_column = cursor[2]
  239.    column = init_column
  240.    input_string = ""
  241.    while TRUE do
  242.     char = wait_key()
  243.     
  244.     if char = CR then
  245.         exit
  246.         
  247.     elsif char = BS or char = ARROW_LEFT then
  248.         if column > init_column then
  249.         column = column - 1
  250.         position(line, column)
  251.         puts(SCREEN, ' ')
  252.         position(line, column)
  253.         input_string = input_string[1..length(input_string)-1]
  254.         end if
  255.         
  256.     elsif char >= 32 and char <= 127 or char = '\t' then
  257.         if column < SCREEN_WIDTH then
  258.         if char = '\t' then
  259.             puts(SCREEN, ' ')
  260.         else
  261.             puts(SCREEN, char)
  262.         end if
  263.         column = column + 1
  264.         if column - init_column > length(input_string) then
  265.             input_string = append(input_string, char)
  266.             if column = init_column + 1 and find(char, hot_keys) then
  267.             exit
  268.             end if
  269.         else
  270.             input_string[column - init_column] = char
  271.         end if
  272.         end if
  273.     end if
  274.     end while
  275.     return input_string
  276. end function
  277.  
  278. procedure set_position(natural window_line, positive_int column)
  279. -- convert relative position within window to absolute screen position
  280. -- window_line 0 is status line, window_line 1 is first line of text
  281.     position(window_base + window_line, column)
  282. end procedure
  283.  
  284. natural edit_tab_width 
  285.  
  286. function tab(natural tab_width, positive_int pos)
  287. -- compute new column position after a tab
  288.     return (floor((pos - 1) / tab_width) + 1) * tab_width + 1
  289. end function
  290.  
  291. function expand_tabs(natural tab_width, sequence line)
  292. -- replace tabs by blanks in a line of text
  293.     natural tab_pos, column, ntabs
  294.  
  295.     column = 1
  296.     while TRUE do
  297.     tab_pos = find('\t', line[column..length(line)])
  298.     if tab_pos = 0 then
  299.         -- no more tabs
  300.         return line
  301.     else
  302.         tab_pos = tab_pos + column - 1
  303.     end if
  304.     column = tab(tab_width, tab_pos)
  305.     ntabs = 1
  306.     while line[tab_pos+ntabs] = '\t' do
  307.         ntabs = ntabs + 1
  308.         column = column + tab_width
  309.     end while
  310.     -- replace consecutive tabs by blanks
  311.     line = line[1..tab_pos-1] & 
  312.            repeat(' ', column - tab_pos) &
  313.            line[tab_pos+ntabs..length(line)]            
  314.     end while
  315. end function
  316.  
  317. function indent_tabs(natural tab_width, sequence line)
  318. -- replace leading blanks of a line with tabs
  319.     natural i, blanks
  320.  
  321.     if length(line) < tab_width then
  322.     return line
  323.     end if
  324.     i = 1
  325.     while line[i] = ' ' do
  326.     i = i + 1
  327.     end while    
  328.     blanks = i - 1    
  329.     return repeat('\t', floor(blanks / tab_width)) & 
  330.        BLANK_LINE[1..remainder(blanks, tab_width)] &
  331.        line[i..length(line)]
  332. end function
  333.  
  334. function convert_tabs(natural old_width, natural new_width, sequence line)
  335. -- retabulate a line for a new tab size
  336.     if old_width = new_width then
  337.     return line
  338.     end if
  339.     return indent_tabs(new_width, expand_tabs(old_width, line))
  340. end function
  341.  
  342. -- color display of lines
  343. include syncolor.e
  344.  
  345. procedure reverse_video()
  346. -- start inverse video
  347.     text_color(TOP_LINE_TEXT_COLOR)
  348.     bk_color(TOP_LINE_BACK_COLOR)
  349. end procedure
  350.  
  351. procedure normal_video()
  352. -- end inverse video
  353.     text_color(NORMAL_COLOR)
  354.     bk_color(BACKGROUND_COLOR)
  355. end procedure
  356.  
  357. procedure ClearLine(window_line sline)
  358. -- clear the current line on screen
  359.     scroll(1, window_base + sline, window_base + sline)
  360. end procedure
  361.  
  362. procedure ClearWindow()
  363. -- clear the current window
  364.     scroll(window_length, window_base+1, window_base+window_length)
  365. end procedure
  366.  
  367. procedure ScrollUp(positive_int top, positive_int bottom)
  368. -- move text up one line on screen
  369.     scroll(+1, window_base + top, window_base + bottom)
  370. end procedure
  371.  
  372. procedure ScrollDown(positive_int top, positive_int bottom)
  373. -- move text down one line on screen
  374.     scroll(-1, window_base + top, window_base + bottom)
  375. end procedure
  376.  
  377. procedure DisplayLine(buffer_line bline, window_line sline, boolean all_clear)
  378. -- display a buffer line on a given line on the screen
  379.     sequence this_line
  380.     natural last
  381.     
  382.     this_line = expand_tabs(edit_tab_width, buffer[bline])
  383.     last = length(this_line) - 1
  384.     set_position(sline, 1)
  385.     if multi_color then
  386.     -- color display
  387.     DisplayColorLine(this_line)
  388.     else
  389.     -- monochrome display
  390.     puts(SCREEN, this_line[1..last])
  391.     end if
  392.     if last > SCREEN_WIDTH then
  393.     -- line too long 
  394.     set_position(sline, SCREEN_WIDTH)
  395.     text_color(BACKGROUND_COLOR)
  396.     bk_color(NORMAL_COLOR)
  397.     puts(SCREEN, this_line[SCREEN_WIDTH])
  398.     normal_video()
  399.     elsif not all_clear then
  400.     puts(SCREEN, BLANK_LINE)
  401.     end if
  402. end procedure
  403.  
  404. procedure DisplayWindow(positive_int bline, window_line sline)
  405. -- print a series of buffer lines, starting at sline on screen
  406. -- and continue until the end of screen, or end of buffer
  407.     boolean all_clear
  408.     
  409.     if sline = 1 then
  410.     ClearWindow()
  411.     all_clear = TRUE
  412.     else
  413.     all_clear = FALSE
  414.     end if
  415.  
  416.     for b = bline to length(buffer) do
  417.     DisplayLine(b, sline, all_clear)
  418.     if sline = window_length then
  419.         return
  420.     else
  421.         sline = sline + 1
  422.     end if
  423.     end for
  424.     -- blank any remaining screen lines after end of file
  425.     for s = sline to window_length do
  426.     ClearLine(s)
  427.     end for
  428. end procedure
  429.  
  430. function clean(sequence line)
  431. -- replace control characters with a graphics character
  432.     integer c
  433.     
  434.     for i = 1 to length(line) do
  435.     c = line[i]
  436.     if c < 32 then
  437.         if c != '\n' then
  438.         if c != '\t' then
  439.             line[i] = CONTROL_CHAR
  440.             control_chars = TRUE
  441.         end if
  442.         end if
  443.     end if
  444.     end for
  445.     if line[length(line)] != '\n' then
  446.     line = line & '\n'
  447.     end if
  448.     return line
  449. end function
  450.  
  451. function add_line(file_number file_no)
  452. -- add a new line to the buffer
  453.     object line
  454.  
  455.     line = gets(file_no)
  456.     
  457.     if atom(line) then
  458.     -- end of file
  459.     return FALSE 
  460.     end if
  461.     
  462.     line = convert_tabs(STANDARD_TAB_WIDTH, edit_tab_width, clean(line))
  463.     buffer = append(buffer, line)
  464.     return TRUE
  465. end function
  466.  
  467. procedure new_buffer()
  468. -- make room for a new (empty) buffer
  469.     buffer_list = buffer_list & 0 -- place holder for new buffer
  470.     buffer_number = length(buffer_list) 
  471.     buffer = {}
  472. end procedure
  473.  
  474. procedure read_file(file_number file_no)
  475. -- read the entire file into buffer variable
  476.     
  477.     -- read and immediately display the first screenful
  478.     for i = 1 to window_length do
  479.     if not add_line(file_no) then
  480.         exit
  481.     end if
  482.     end for
  483.     DisplayWindow(1, 1)
  484.  
  485.     -- read the rest
  486.     while add_line(file_no) do
  487.     end while
  488.  
  489. end procedure
  490.  
  491. procedure set_top_line(sequence message)
  492. -- print message on top line
  493.     set_position(0, 1)
  494.     reverse_video()
  495.     puts(SCREEN, message & BLANK_LINE)
  496.     set_position(0, length(message)+1)
  497. end procedure
  498.  
  499. procedure save_file(sequence file_name)
  500. -- write buffer back into the disk file
  501.     file_number file_no
  502.     
  503.     if control_chars then
  504.     set_top_line("")
  505.     printf(SCREEN, "%s: control chars were changed to " & CONTROL_CHAR &
  506.                " - save anyway? ", {file_name})
  507.     if not find('y', key_gets("yn")) then
  508.         stop = FALSE
  509.         return
  510.     end if
  511.     end if
  512.     set_top_line("")
  513.     file_no = open(file_name, "w")
  514.     if file_no = -1 then
  515.     printf(SCREEN, "Can't save %s - write permission denied", 
  516.           {file_name})
  517.     stop = FALSE
  518.     return
  519.     end if
  520.     printf(SCREEN, "saving %s ... ", {file_name})
  521.     for i = 1 to length(buffer) do
  522.     puts(file_no, 
  523.          convert_tabs(edit_tab_width, STANDARD_TAB_WIDTH, buffer[i]))
  524.     end for
  525.     close(file_no)
  526.     puts(SCREEN, "ok")
  527.     clear_modified()
  528.     stop = TRUE
  529. end procedure
  530.  
  531. procedure arrow_right()
  532. -- action for right arrow key
  533.  
  534.     positive_int temp_col
  535.  
  536.     if b_col < length(buffer[b_line]) then
  537.     if buffer[b_line][b_col] = '\t' then
  538.         temp_col = tab(edit_tab_width, s_col)
  539.     else
  540.         temp_col = s_col + 1
  541.     end if
  542.     if temp_col > SCREEN_WIDTH then
  543.         return
  544.     end if
  545.     s_col = temp_col
  546.     b_col = b_col + 1
  547.     end if
  548. end procedure
  549.  
  550. procedure arrow_left()
  551. -- action for left arrow key
  552.  
  553.     positive_int old_b_col
  554.  
  555.     old_b_col = b_col
  556.     b_col = 1
  557.     s_col = 1
  558.     for i = 1 to old_b_col - 2 do
  559.     arrow_right()
  560.     end for
  561. end procedure
  562.     
  563. procedure skip_white()
  564. -- set cursor to first non-whitespace in line    
  565.     positive_int temp_col
  566.     
  567.     while find(buffer[b_line][b_col], " \t") do
  568.     temp_col = s_col
  569.     arrow_right()
  570.     if s_col = temp_col then
  571.         return -- can't move any further right
  572.     end if
  573.     end while
  574. end procedure
  575.  
  576. procedure arrow_up()
  577. -- action for up arrow key
  578.  
  579.     b_col = 1
  580.     s_col = 1
  581.     if b_line > 1 then
  582.     b_line = b_line - 1
  583.     if s_line > 1 then
  584.         s_line = s_line - 1
  585.     else
  586.         -- move all lines down, display new line at top
  587.         ScrollDown(1, window_length)
  588.         DisplayLine(b_line, 1, TRUE)
  589.         set_position(1, 1)
  590.         s_line = 1
  591.     end if
  592.     skip_white()
  593.     end if
  594. end procedure
  595.  
  596. procedure arrow_down()
  597. -- action for down arrow key
  598.     b_col = 1
  599.     s_col = 1
  600.     if b_line < length(buffer) then
  601.     b_line = b_line + 1
  602.     if s_line < window_length then
  603.         s_line = s_line + 1
  604.     else
  605.         -- move all lines up, display new line at bottom
  606.         ScrollUp(1, window_length)
  607.         DisplayLine(b_line, window_length, TRUE)
  608.     end if
  609.     skip_white()
  610.     end if
  611. end procedure
  612.  
  613. function numeric(sequence string)
  614. -- convert digit string to an integer
  615.     atom n
  616.  
  617.     n = 0
  618.     for i = 1 to length(string) do
  619.     if string[i] >= '0' and string[i] <= '9' then
  620.         n = n * 10 + string[i] - '0'
  621.         if not integer(n) then
  622.         return 0
  623.         end if
  624.     else
  625.         exit
  626.     end if
  627.     end for
  628.     return n
  629. end function
  630.  
  631. procedure goto_line(integer new_line, integer new_col)
  632. -- move to a specified line and column
  633. -- refresh screen if line is 0
  634.     integer new_s_line
  635.     boolean refresh
  636.  
  637.     if length(buffer) = 0 then
  638.     ClearWindow()
  639.     s_line = 1
  640.     s_col = 1
  641.     return
  642.     end if
  643.     if new_line = 0 then
  644.     new_line = b_line
  645.     refresh = TRUE
  646.     else
  647.     refresh = FALSE
  648.     end if
  649.     if new_line < 1 then
  650.     new_line = 1
  651.     elsif new_line > length(buffer) then
  652.     new_line = length(buffer)
  653.     end if
  654.     new_s_line = new_line - b_line + s_line
  655.     b_line = new_line
  656.     if not refresh and window_line(new_s_line) then
  657.     -- new line is on the screen
  658.     s_line = new_s_line
  659.     else
  660.     -- new line is off the screen, or refreshing
  661.     set_position(1, 1)
  662.     s_line = floor((window_length+1)/2)
  663.     if s_line > b_line or length(buffer) < window_length then
  664.         s_line = b_line
  665.     elsif b_line > length(buffer) - window_length + s_line then
  666.         s_line = window_length - (length(buffer) - b_line)
  667.     end if
  668.     DisplayWindow(b_line - s_line + 1, 1)
  669.     end if
  670.     b_col = 1
  671.     s_col = 1
  672.     set_position(s_line, s_col)
  673.     for i = 1 to new_col-1 do
  674.     arrow_right()
  675.     end for
  676. end procedure
  677.  
  678. procedure page_down()
  679. -- action for page-down key
  680.     buffer_line prev_b_line
  681.  
  682.     if length(buffer) <= window_length then
  683.     return
  684.     end if
  685.     prev_b_line = b_line
  686.     b_col = 1
  687.     s_col = 1
  688.     if b_line + window_length + window_length - s_line <= length(buffer) then
  689.     b_line = b_line + window_length
  690.     else
  691.     b_line = length(buffer) - (window_length - s_line)
  692.     end if
  693.     if b_line != prev_b_line then
  694.     DisplayWindow(b_line - s_line + 1, 1)
  695.     end if
  696. end procedure
  697.  
  698. procedure page_up()
  699. -- action for page-up key
  700.     buffer_line prev_b_line
  701.  
  702.     if length(buffer) <= window_length then
  703.     return
  704.     end if
  705.     prev_b_line = b_line
  706.     b_col = 1
  707.     s_col = 1
  708.     if b_line - window_length >= s_line then
  709.     b_line = b_line - window_length
  710.     else
  711.     b_line = s_line
  712.     end if
  713.     if b_line != prev_b_line then
  714.     DisplayWindow(b_line - s_line + 1, 1)
  715.     end if
  716. end procedure
  717.  
  718. procedure set_f_line(natural w, sequence comment)
  719. -- show F-key & file_name
  720.     sequence f_key, text
  721.     
  722.     if length(window_list) = 1 then
  723.     f_key = ""
  724.     elsif w = 10 then
  725.     f_key = "F10: "
  726.     else
  727.     f_key = 'F' & ('0' + w) & ": "
  728.     end if
  729.     set_top_line(' ' & f_key & file_name & comment)
  730.     text = "Esc for commands"
  731.     set_position(0, SCREEN_WIDTH - length(text))
  732.     puts(SCREEN, text)
  733. end procedure
  734.  
  735. constant W_BUFFER_NUMBER = 1,
  736.      W_MY_BUFFER_VERSION = 2,
  737.      W_WINDOW_BASE = 3,
  738.      W_WINDOW_LENGTH = 4,
  739.      W_B_LINE = 10
  740.  
  741. procedure save_state()
  742. -- save current state variables for a window
  743.     window_list[window_number] = {buffer_number, buffer_version, window_base, 
  744.                   window_length, auto_complete, multi_color, 
  745.                   dot_e, control_chars, file_name, b_line, 
  746.                   b_col, s_line, s_col, edit_tab_width}
  747.     buffer_list[buffer_number] = {buffer, modified, buffer_version}
  748. end procedure
  749.  
  750. procedure restore_state(window_id w)
  751. -- restore state variables for a window
  752.     sequence state
  753.     sequence buffer_info
  754.     
  755.     -- set up new buffer
  756.     state = window_list[w]
  757.     window_number = w
  758.     buffer_number =  state[W_BUFFER_NUMBER]
  759.     buffer_info = buffer_list[buffer_number]
  760.     buffer = buffer_info[1]
  761.     modified = buffer_info[2]
  762.     buffer_version = buffer_info[3]
  763.     buffer_list[buffer_number] = 0 -- save space
  764.     
  765.     -- restore other variables
  766.     my_buffer_version = state[2]
  767.     window_base = state[3]
  768.     window_length = state[4]
  769.     auto_complete = state[5]
  770.     multi_color = state[6]
  771.     dot_e = state[7]
  772.     control_chars = state[8]
  773.     file_name = state[9]
  774.     edit_tab_width = state[14]
  775.     set_f_line(w, "")
  776.     normal_video()
  777.     if buffer_version != my_buffer_version then
  778.     -- buffer has changed since we were last in this window
  779.     -- or window size has changed
  780.     if state[W_B_LINE] > length(buffer) then
  781.         if length(buffer) = 0 then
  782.         b_line = 1
  783.         else
  784.         b_line = length(buffer)
  785.         end if
  786.     else
  787.         b_line = state[W_B_LINE]
  788.     end if
  789.     goto_line(0, 1)
  790.     else
  791.     b_line = state[W_B_LINE]
  792.     b_col = state[11]
  793.     s_line = state[12]
  794.     s_col = state[13]
  795.     set_position(s_line, s_col)
  796.     end if
  797. end procedure
  798.  
  799. procedure refresh_other_windows(positive_int w)
  800. -- redisplay all windows except w
  801.     
  802.     normal_video()
  803.     for i = 1 to length(window_list) do
  804.     if i != w then
  805.         restore_state(i)
  806.         set_f_line(i, "")
  807.         normal_video()
  808.         goto_line(0, b_col)
  809.         save_state()
  810.     end if
  811.     end for
  812. end procedure
  813.  
  814. procedure set_window_size()
  815. -- set sizes for windows
  816.     natural nwindows, lines, base, size
  817.     
  818.     nwindows = length(window_list)
  819.     lines = screen_length - nwindows
  820.     base = 1
  821.     for i = 1 to length(window_list) do
  822.     size = floor(lines / nwindows)
  823.     window_list[i][W_WINDOW_BASE] = base
  824.     window_list[i][W_WINDOW_LENGTH] = size
  825.     window_list[i][W_MY_BUFFER_VERSION] = -1 -- force redisplay
  826.     base = base + size + 1
  827.     nwindows = nwindows - 1
  828.     lines = lines - size
  829.     end for
  830. end procedure
  831.  
  832. procedure clone_window()
  833. -- set up a new window that is a clone of the current window
  834. -- save state of current window
  835.     window_id w
  836.     
  837.     if length(window_list) >= MAX_WINDOWS then
  838.     return
  839.     end if
  840.     save_state()
  841.     -- create a place for new window
  842.     window_list = window_list[1..window_number] & 
  843.           {window_list[window_number]} &  -- the new clone window
  844.           window_list[window_number+1..length(window_list)]
  845.     w = window_number + 1
  846.     set_window_size()
  847.     refresh_other_windows(w)
  848.     restore_state(w) 
  849. end procedure
  850.  
  851. procedure switch_window(integer new_window_number)
  852. -- switch context to a new window on the screen
  853.     
  854.     if new_window_number != window_number then
  855.     save_state()
  856.     restore_state(new_window_number)
  857.     end if
  858. end procedure
  859.  
  860. function delete_window()
  861. -- delete the current window    
  862.     boolean buff_in_use
  863.     
  864.     buffer_list[buffer_number] = {buffer, modified, buffer_version}
  865.     window_list = window_list[1..window_number-1] & 
  866.           window_list[window_number+1..length(window_list)]
  867.     buff_in_use = FALSE
  868.     for i = 1 to length(window_list) do
  869.     if window_list[i][W_BUFFER_NUMBER] = buffer_number then
  870.         buff_in_use = TRUE
  871.         exit
  872.     end if
  873.     end for 
  874.     if not buff_in_use then
  875.     buffer_list[buffer_number] = 0 -- discard the buffer
  876.     end if
  877.     if length(window_list) = 0 then
  878.     return TRUE
  879.     end if
  880.     set_window_size()
  881.     refresh_other_windows(1)
  882.     window_number = 1
  883.     restore_state(window_number)
  884.     set_position(s_line, s_col)
  885.     return FALSE
  886. end function
  887.  
  888. procedure new_screen_length()
  889. -- set new number of lines on screen
  890.     natural nlines
  891.     window_id w
  892.     
  893.     set_top_line("How many lines on screen? (25, 28, 43, 50) ")
  894.     nlines = numeric(key_gets(""))
  895.     if nlines then
  896.     screen_length = text_rows(nlines)
  897.     if screen_length != nlines then
  898.         sound(500)
  899.     end if
  900.     w = window_number
  901.     save_state()
  902.     set_window_size()
  903.     refresh_other_windows(w)
  904.     restore_state(w)
  905.     if screen_length != nlines then
  906.         sound(0)
  907.     end if
  908.     end if
  909. end procedure
  910.  
  911. -- searching/replacing variables
  912. boolean searching, replacing, match_case
  913. searching = FALSE
  914. replacing = FALSE
  915. match_case = TRUE
  916.  
  917. sequence find_string -- current (default) string to look for
  918. find_string = ""
  919.  
  920. sequence replace_string -- current (default) string to replace with
  921. replace_string = ""
  922.  
  923. procedure replace()
  924. -- replace find_string by replace_string
  925. -- we are currently positioned at the start of an occurrence of find_string
  926.     sequence line
  927.  
  928.     set_modified()
  929.     line = buffer[b_line]
  930.     line = line[1..b_col-1] & replace_string & line[b_col+length(find_string)..
  931.                         length(line)]
  932.     buffer[b_line] = line
  933.     -- position at end of replacement string
  934.     for i = 1 to length(replace_string)-1 do
  935.     arrow_right()
  936.     end for
  937.     DisplayLine(b_line, s_line, FALSE)
  938. end procedure
  939.  
  940. function lower(sequence s)
  941. -- convert to lower case
  942.     integer c
  943.     for i = 1 to length(s) do
  944.     c = s[i]
  945.     if c >= 'A' then
  946.         if c <= 'Z' then
  947.         s[i] = c + 'a' - 'A' 
  948.         end if
  949.     end if
  950.     end for
  951.     return s
  952. end function
  953.  
  954. function alphabetic(object s)
  955. -- does s contain alphabetic characters?
  956.     return find(TRUE, (s >= 'A' and s <= 'Z') or
  957.               (s >= 'a' and s <= 'z')) 
  958. end function
  959.  
  960. function case_match(sequence string, sequence text)
  961. -- Find string in text with
  962. -- either case-sensitive or non-case-sensitive comparison
  963.     if match_case then
  964.     return match(string, text)
  965.     else
  966.     return match(lower(string), lower(text))
  967.     end if
  968. end function
  969.  
  970. function search(boolean continue)
  971. -- find a string from here to the end of the file
  972. -- return TRUE if string is found
  973.     natural col
  974.     sequence temp_find_string, temp_replace_string
  975.     sequence pos
  976.     
  977.     set_top_line("")
  978.     if length(buffer) = 0 then
  979.     puts(SCREEN, "buffer empty")
  980.     return FALSE
  981.     end if
  982.     if length(find_string) = 0 then
  983.     puts(SCREEN, "searching for:")
  984.     else
  985.     printf(SCREEN, "searching for \"%s\":", {find_string})
  986.     end if
  987.     if not continue then
  988.     pos = get_position()
  989.     temp_find_string = key_gets("")
  990.     if length(temp_find_string) > 0 then
  991.         -- new string typed in
  992.         find_string = temp_find_string
  993.         if alphabetic(find_string) then
  994.         set_position(0, pos[2]+length(temp_find_string)+3)
  995.         puts(SCREEN, "match case? y")
  996.         pos = get_position()
  997.         set_position(0, pos[2] - 1)
  998.         match_case = not find('n', key_gets(""))
  999.         end if
  1000.     end if
  1001.     if replacing then
  1002.         set_top_line("")
  1003.         if length(replace_string) = 0 then
  1004.         puts(SCREEN, "replace with:")
  1005.         else
  1006.         printf(SCREEN, "replace with \"%s\":", {replace_string})
  1007.         end if
  1008.         temp_replace_string = key_gets("")
  1009.         if length(temp_replace_string) > 0 then
  1010.         replace_string = temp_replace_string
  1011.         end if
  1012.     end if
  1013.     end if
  1014.  
  1015.     normal_video()
  1016.     if length(find_string) = 0 then
  1017.     return FALSE
  1018.     end if
  1019.     col = case_match(find_string, buffer[b_line][b_col+1..length(buffer[b_line])])
  1020.     if col and s_col < SCREEN_WIDTH then
  1021.     -- found it on this line after current position
  1022.     for i = 1 to col do
  1023.         arrow_right()
  1024.     end for
  1025.     if replacing then
  1026.         replace()
  1027.     end if
  1028.     return TRUE
  1029.     else
  1030.     -- check lines following this one
  1031.     for b = b_line+1 to length(buffer) do
  1032.         col = case_match(find_string, buffer[b])
  1033.         if col then
  1034.         goto_line(b, 1)
  1035.         for i = 1 to col - 1 do
  1036.            arrow_right()
  1037.         end for
  1038.         if replacing and s_col < SCREEN_WIDTH then
  1039.             replace()
  1040.         end if
  1041.         set_top_line("")
  1042.         printf(SCREEN, "searching for \"%s\":", {find_string})
  1043.         return TRUE
  1044.         end if
  1045.     end for
  1046.     set_top_line("")
  1047.     printf(SCREEN, "\"%s\" not found", {find_string})
  1048.     if alphabetic(find_string) then
  1049.         if match_case then
  1050.         puts(SCREEN, "  (case must match)")
  1051.         else
  1052.         puts(SCREEN, "  (any case)")
  1053.         end if
  1054.     end if
  1055.     end if
  1056.     return FALSE
  1057. end function
  1058.  
  1059. procedure show_message()
  1060. -- display error message from ex.err
  1061.     if length(error_message) > 0 then
  1062.     set_top_line(error_message)
  1063.     normal_video()
  1064.     end if
  1065.     set_position(s_line, s_col)
  1066. end procedure
  1067.  
  1068. procedure set_err_pointer()
  1069. -- set cursor at point of error 
  1070.  
  1071.     for i = 1 to SCREEN_WIDTH do
  1072.     if s_col >= start_col then
  1073.         exit
  1074.     end if
  1075.     arrow_right()
  1076.     end for
  1077. end procedure
  1078.  
  1079. function delete_trailing_white(sequence name)
  1080. -- get rid of blanks, tabs, newlines at end of string
  1081.     while length(name) > 0 do
  1082.     if find(name[length(name)], "\n\r\t ") then
  1083.         name = name[1..length(name)-1]
  1084.     else
  1085.         exit
  1086.     end if
  1087.     end while
  1088.     return name
  1089. end function
  1090.  
  1091. function get_err_line()
  1092. -- try to get file name & line number from ex.err
  1093. -- returns file_name, sets start_line, start_col, error_message
  1094.  
  1095.     file_number err_file
  1096.     sequence file_name
  1097.     sequence err_lines
  1098.     object temp_line
  1099.     natural colon_pos
  1100.  
  1101.     err_file = open("ex.err", "r")
  1102.     if err_file = -1 then
  1103.     error_message = ""
  1104.     else
  1105.     -- read the top of the ex.err error message file
  1106.     err_lines = {}
  1107.     while length(err_lines) < 5 do
  1108.         temp_line = gets(err_file)
  1109.         if atom(temp_line) then
  1110.         exit
  1111.         end if
  1112.         err_lines = append(err_lines, temp_line)
  1113.     end while
  1114.     close(err_file)
  1115.     -- look for file name, line, column and error message
  1116.     if length(err_lines) > 0 then
  1117.         if sequence(err_lines[1]) then
  1118.         colon_pos = match(".e", err_lines[1])
  1119.         if colon_pos then
  1120.             if err_lines[1][colon_pos+2] = 'x' then
  1121.             colon_pos = colon_pos + 1
  1122.             end if
  1123.             file_name = err_lines[1][1..colon_pos+1]
  1124.             start_line = numeric(err_lines[1][colon_pos+3..
  1125.                               length(err_lines[1])])
  1126.             error_message = delete_trailing_white(err_lines[2])
  1127.             if length(err_lines) > 3 then
  1128.             start_col = find('^', expand_tabs(STANDARD_TAB_WIDTH, 
  1129.                           err_lines[length(err_lines)-1]))
  1130.             end if
  1131.             return file_name
  1132.         end if
  1133.         end if
  1134.     end if
  1135.     end if
  1136.     return ""
  1137. end function
  1138.  
  1139. function last_use()
  1140. -- return TRUE if current buffer 
  1141. -- is only referenced by the current window
  1142.     natural count
  1143.     
  1144.     count = 0
  1145.     for i = 1 to length(window_list) do
  1146.     if window_list[i][W_BUFFER_NUMBER] = buffer_number then
  1147.         count = count + 1
  1148.         if count = 2 then
  1149.         return FALSE
  1150.         end if
  1151.     end if
  1152.     end for
  1153.     return TRUE
  1154. end function
  1155.  
  1156. procedure shell(sequence command, boolean wait)
  1157. -- run a DOS command
  1158.     window_id w
  1159.     
  1160.     bk_color(BLACK)
  1161.     text_color(WHITE)
  1162.     clear_screen()
  1163.     system(command, wait)
  1164.     normal_video()
  1165.     while get_key() != -1 do
  1166.     -- clear the keyboard buffer
  1167.     end while
  1168.     w = window_number
  1169.     save_state()
  1170.     refresh_other_windows(w)
  1171.     restore_state(w)
  1172. end procedure
  1173.  
  1174. procedure first_bold(sequence string)
  1175. -- highlight first char
  1176.     text_color(TOP_LINE_TEXT_COLOR)
  1177.     puts(SCREEN, string[1])
  1178.     text_color(TOP_LINE_DIM_COLOR)
  1179.     puts(SCREEN, string[2..length(string)])
  1180. end procedure
  1181.  
  1182. procedure get_escape(boolean help)
  1183. -- process escape command
  1184.     sequence command, answer, temp_name
  1185.     natural line
  1186.     object dos_command
  1187.  
  1188.     cursor(ED_CURSOR)
  1189.  
  1190.     set_top_line("")
  1191.     if help then
  1192.     command = "h"
  1193.     else
  1194.     first_bold("help  ")
  1195.     first_bold("clone  ")
  1196.     first_bold("quit|")
  1197.     first_bold("save|")
  1198.     first_bold("write|")
  1199.     first_bold("new  ")
  1200.     if dot_e then
  1201.         first_bold("ex|")
  1202.     end if
  1203.     first_bold("dos  ")
  1204.     first_bold("find|")
  1205.     first_bold("replace  ")
  1206.     first_bold("lines  ")
  1207.     text_color(TOP_LINE_TEXT_COLOR)
  1208.     puts(SCREEN, "ddd  CR: ")
  1209.     command = key_gets("hcqswnedfrl") & ' '
  1210.     end if
  1211.  
  1212.     if command[1] = 'f' then
  1213.     replacing = FALSE
  1214.     searching = search(FALSE)
  1215.  
  1216.     elsif command[1] = 'r' then
  1217.     replacing = TRUE
  1218.     searching = search(FALSE)
  1219.  
  1220.     elsif command[1] = 'q' then
  1221.     if modified and last_use() then
  1222.         set_top_line("quit without saving changes? ")
  1223.         if find('y', key_gets("yn")) then
  1224.         stop = delete_window()
  1225.         end if
  1226.     else
  1227.         stop = delete_window()
  1228.     end if
  1229.     
  1230.     elsif command[1] = 'c' then
  1231.     clone_window()
  1232.     
  1233.     elsif command[1] = 'n' then
  1234.     if modified and last_use() then
  1235.         set_top_line("")
  1236.         printf(SCREEN, "save changes to %s? ", {file_name})
  1237.         if find('y', key_gets("yn")) then
  1238.         save_file(file_name)
  1239.         end if
  1240.     end if
  1241.     save_state()
  1242.     set_top_line("new file name? ")
  1243.     temp_name = delete_trailing_white(key_gets(""))
  1244.     if length(temp_name) != 0 then
  1245.         file_name = temp_name
  1246.         stop = TRUE
  1247.     end if
  1248.  
  1249.     elsif command[1] = 'w' then
  1250.     save_file(file_name)
  1251.     stop = FALSE
  1252.  
  1253.     elsif command[1] = 's' then
  1254.     save_file(file_name)
  1255.     if stop then
  1256.         stop = delete_window()
  1257.     end if
  1258.  
  1259.     elsif command[1] = 'e' and dot_e then
  1260.     if modified then
  1261.         save_file(file_name)
  1262.         stop = FALSE
  1263.     end if
  1264.     -- execute the current file & return
  1265.     if sequence(dir("ex.err")) then
  1266.         system("del ex.err > NUL", 0)
  1267.     end if
  1268.     shell("ex " & file_name, TRUE)
  1269.     goto_line(0, b_col)
  1270.     if compare(file_name, get_err_line()) = 0 then
  1271.         goto_line(start_line, 1)
  1272.         set_err_pointer()
  1273.         show_message()
  1274.     end if
  1275.  
  1276.     elsif command[1] = 'd' then
  1277.     set_top_line("DOS command? ")
  1278.     dos_command = key_gets("")
  1279.     shell(dos_command, TRUE)
  1280.     goto_line(0, b_col) -- refresh screen
  1281.  
  1282.     elsif command[1] = 'h' then
  1283.     dos_command = getenv("EUDIR")
  1284.     if atom(dos_command) then
  1285.         -- Euphoria hasn't been installed yet 
  1286.         set_top_line("EUDIR not set. See install.doc")
  1287.     else    
  1288.         dos_command = "ed.bat " & dos_command & "\\DOC"
  1289.         if help then
  1290.         set_top_line(
  1291.         "That key does nothing - do you want to view the help text? ")
  1292.         answer = key_gets("yn") & ' '
  1293.         if answer[1] != 'n' and answer[1] != 'N' then
  1294.             answer = "e"
  1295.         end if
  1296.         else
  1297.         set_top_line("ed.doc, refman.doc, or library.doc? (e, r or l): ")
  1298.         answer = key_gets("erl") & ' '
  1299.         end if
  1300.         if answer[1] = 'r' then
  1301.         shell(dos_command & "\\REFMAN.DOC", FALSE)
  1302.         goto_line(0, b_col)
  1303.         elsif answer[1] = 'e' then
  1304.         shell(dos_command & "\\ED.DOC", FALSE)
  1305.         goto_line(0, b_col)
  1306.         elsif answer[1] = 'l' then
  1307.         shell(dos_command & "\\LIBRARY.DOC", FALSE)
  1308.         goto_line(0, b_col)
  1309.         else
  1310.         normal_video()
  1311.         end if
  1312.     end if
  1313.  
  1314.     elsif command[1] = 'l' then
  1315.     new_screen_length()
  1316.  
  1317.     elsif command[1] >= '0' and command[1] <= '9' then
  1318.     line = numeric(command)
  1319.     normal_video()
  1320.     goto_line(line, 1)
  1321.     if not buffer_line(line) then
  1322.         set_top_line("")
  1323.         printf(SCREEN, "lines are 1..%d", length(buffer))
  1324.     end if
  1325.  
  1326.     else
  1327.     set_top_line("")
  1328.     if length(buffer) = 0 then
  1329.         puts(SCREEN, "empty buffer")
  1330.     else
  1331.         printf(SCREEN, "%s line %d of %d, column %d of %d, ",
  1332.                {file_name, b_line, length(buffer), s_col,
  1333.             length(expand_tabs(edit_tab_width, buffer[b_line]))-1})
  1334.         if modified then
  1335.         puts(SCREEN, "modified")
  1336.         else
  1337.         puts(SCREEN, "not modified")
  1338.         end if
  1339.     end if
  1340.     end if
  1341.  
  1342.     normal_video()
  1343. end procedure
  1344.  
  1345. procedure insert(char key)
  1346. -- insert a character into the current line at the current position
  1347.  
  1348.     sequence tail
  1349.     positive_int new_col
  1350.  
  1351.     set_modified()
  1352.     tail = buffer[b_line][b_col..length(buffer[b_line])]
  1353.     if key = CR or key = '\n' then
  1354.     -- truncate this line and create a new line using tail
  1355.     buffer[b_line] = buffer[b_line][1..b_col-1] & '\n'
  1356.     buffer = append(buffer[1..b_line], tail) &
  1357.             buffer[b_line+1..length(buffer)]
  1358.     if s_line = window_length then
  1359.         arrow_down()
  1360.         arrow_up()
  1361.     else
  1362.         ScrollDown(s_line+1, window_length)
  1363.     end if
  1364.     if window_length = 1 then
  1365.         arrow_down()
  1366.     else
  1367.         DisplayLine(b_line, s_line, FALSE)
  1368.         b_line = b_line + 1
  1369.         s_line = s_line + 1
  1370.         DisplayLine(b_line, s_line, FALSE)
  1371.     end if
  1372.     s_col = 1
  1373.     b_col = 1
  1374.     else
  1375.     if key = '\t' then
  1376.         new_col = tab(edit_tab_width, s_col)
  1377.     else
  1378.         new_col = s_col + 1
  1379.     end if
  1380.     if new_col > SCREEN_WIDTH then
  1381.         return
  1382.     else
  1383.         s_col = new_col
  1384.     end if
  1385.     buffer[b_line] = buffer[b_line][1..b_col-1] & key & tail
  1386.     DisplayLine(b_line, s_line, TRUE)
  1387.     b_col = b_col + 1
  1388.     end if
  1389.     set_position(s_line, s_col)
  1390. end procedure
  1391.  
  1392. procedure insert_string(sequence text)
  1393. -- insert a bunch of characters at the current position
  1394.     natural save_line, save_col
  1395.  
  1396.     save_line = b_line
  1397.     save_col = b_col
  1398.     for i = 1 to length(text) do
  1399.     if text[i] = CR or text[i] = '\n' then
  1400.         insert(text[i])
  1401.     else
  1402.         buffer[b_line] = buffer[b_line][1..b_col-1] & text[i] &
  1403.                  buffer[b_line][b_col..length(buffer[b_line])]
  1404.         b_col = b_col + 1
  1405.         if i = length(text) then
  1406.         DisplayLine(b_line, s_line, FALSE)
  1407.         end if
  1408.     end if
  1409.     end for
  1410.     goto_line(save_line, save_col)
  1411. end procedure
  1412.  
  1413. -- expandable words & corresponding text
  1414. constant expand_word = {"if", "for", "while", "elsif",
  1415.             "procedure", "type", "function"},
  1416.  
  1417.      expand_text = {" then",  "=  to  by  do",  " do",  " then",
  1418.             "()",  "()",  "()" 
  1419.                }
  1420.  
  1421. procedure try_auto_complete(char key)
  1422. -- check for a keyword that can be automatically completed
  1423.     sequence word, this_line, white_space, leading_white, begin
  1424.     natural first_non_blank, wordnum
  1425.  
  1426.     if key = ' ' then
  1427.     insert(key)
  1428.     end if
  1429.     this_line = buffer[b_line]
  1430.     white_space = this_line = ' ' or this_line = '\t'
  1431.     first_non_blank = find(0, white_space) -- there's always '\n' at end
  1432.     leading_white = this_line[1..first_non_blank - 1]         
  1433.     if auto_complete and first_non_blank < b_col - 2 then
  1434.     if not find(0, white_space[b_col..length(white_space)-1]) then
  1435.         word = this_line[first_non_blank..b_col - 1 - (key = ' ')]
  1436.         wordnum = find(word, expand_word)           
  1437.         
  1438.         if key = CR and compare(word, "else") = 0 then    
  1439.          leading_white = leading_white & '\t'
  1440.         
  1441.         elsif wordnum > 0 then
  1442.         sound(1000)
  1443.         -- expandable word (only word on line)
  1444.  
  1445.         begin = expand_text[wordnum] & CR & leading_white
  1446.         
  1447.         if compare(word, "elsif") = 0 then
  1448.             insert_string(begin & '\t')
  1449.            
  1450.         elsif find(word, {"function", "type"}) then
  1451.             insert_string(begin & CR & 
  1452.                   leading_white & "\treturn" & CR &
  1453.                   "end " & expand_word[wordnum])
  1454.         else
  1455.             insert_string(begin & '\t' & CR &
  1456.                   leading_white &
  1457.                   "end " & expand_word[wordnum])
  1458.         end if
  1459.         delay(0.07) -- or beep is too short
  1460.         sound(0)
  1461.         end if
  1462.     end if
  1463.     end if
  1464.     if key = CR then
  1465.     if b_col >= first_non_blank then
  1466.         buffer[b_line] = buffer[b_line][1..b_col-1] & leading_white &
  1467.                  buffer[b_line][b_col..length(buffer[b_line])]
  1468.         insert(CR)
  1469.         skip_white()
  1470.     else
  1471.         insert(CR)
  1472.     end if
  1473.     end if
  1474. end procedure
  1475.  
  1476. procedure insert_kill_buffer()
  1477. -- insert the kill buffer at the current position
  1478. -- kill buffer could be a sequence of lines or a sequence of characters
  1479.  
  1480.     if length(kill_buffer) = 0 then
  1481.     return
  1482.     end if
  1483.     if atom(kill_buffer[1]) then
  1484.     -- inserting a sequence of chars
  1485.     insert_string(kill_buffer)
  1486.     else
  1487.     -- inserting a sequence of lines
  1488.     set_modified()
  1489.     buffer = buffer[1..b_line - 1] &
  1490.          kill_buffer &
  1491.          buffer[b_line..length(buffer)]
  1492.     DisplayWindow(b_line, s_line)
  1493.     b_col = 1
  1494.     s_col = 1
  1495.     end if
  1496. end procedure
  1497.  
  1498. procedure delete_line(buffer_line dead_line)
  1499. -- delete a line from the buffer and update the display if necessary
  1500.  
  1501.     integer x
  1502.  
  1503.     set_modified()
  1504.     buffer = buffer[1..dead_line-1] & buffer[dead_line+1..length(buffer)]
  1505.     x = dead_line - b_line + s_line
  1506.     if window_line(x) then
  1507.     -- dead line is on the screen at line x
  1508.     ScrollUp(x, window_length)
  1509.     if length(buffer) - b_line >= window_length - s_line then
  1510.         -- show new line at bottom
  1511.         DisplayLine(b_line + window_length - s_line, window_length, TRUE)
  1512.     end if
  1513.     end if
  1514.     if b_line > length(buffer) then
  1515.     arrow_up()
  1516.     else
  1517.     b_col = 1
  1518.     s_col = 1
  1519.     end if
  1520.     adding_to_kill = TRUE
  1521. end procedure
  1522.  
  1523. procedure delete_char()
  1524. -- delete the character at the current position
  1525.     char dchar
  1526.     sequence head
  1527.     natural save_b_col
  1528.  
  1529.     set_modified()
  1530.     dchar = buffer[b_line][b_col]
  1531.     head = buffer[b_line][1..b_col - 1]
  1532.     if dchar = '\n' then
  1533.     if b_line < length(buffer) then
  1534.         -- join this line with the next one and delete the next one
  1535.         buffer[b_line] = head & buffer[b_line+1]
  1536.         DisplayLine(b_line, s_line, FALSE)
  1537.         save_b_col = b_col
  1538.         delete_line(b_line + 1)
  1539.         for i = 1 to save_b_col - 1 do
  1540.         arrow_right()
  1541.         end for
  1542.     else
  1543.         if length(buffer[b_line]) = 1 then
  1544.         delete_line(b_line)
  1545.         else
  1546.         arrow_left() -- a line must always end with \n
  1547.         end if
  1548.     end if
  1549.     else
  1550.     buffer[b_line] = head & buffer[b_line][b_col+1..length(buffer[b_line])]
  1551.     if length(buffer[b_line]) = 0 then
  1552.         delete_line(b_line)
  1553.     else
  1554.         DisplayLine(b_line, s_line, FALSE)
  1555.         if b_col > length(buffer[b_line]) then
  1556.         arrow_left()
  1557.         end if
  1558.     end if
  1559.     end if
  1560.     adding_to_kill = TRUE
  1561. end procedure
  1562.  
  1563. function good(extended_char key)
  1564. -- return TRUE if key should be processed
  1565.     if find(key, CONTROL_CHARS) then
  1566.     return TRUE
  1567.     elsif (key >= ' ' and key <= 127) then
  1568.     return TRUE
  1569.     elsif key = '\t' or key = CR then
  1570.     return TRUE
  1571.     else
  1572.     return FALSE
  1573.     end if
  1574. end function
  1575.  
  1576. procedure edit_file()
  1577. -- edit the file in buffer
  1578.     extended_char key
  1579.  
  1580.     if length(buffer) > 0 then
  1581.     if start_line > 0 then
  1582.         if start_line > length(buffer) then
  1583.         start_line = length(buffer)
  1584.         end if
  1585.         goto_line(start_line, 1)
  1586.         set_err_pointer()
  1587.         show_message()
  1588.     end if
  1589.     end if
  1590.     
  1591.     -- speed up keyboard repeat rate:
  1592.     -- system("mode con rate=30 delay=2", 2)
  1593.     
  1594.     cursor(ED_CURSOR)
  1595.     stop = FALSE
  1596.  
  1597.     while not stop do
  1598.  
  1599.     key = wait_key()
  1600.     
  1601.     if good(key) then
  1602.         -- normal key
  1603.  
  1604.         if key >= F1 and key <= F10 then
  1605.         if key < F1 + length(window_list) then
  1606.             switch_window(key - F1 + 1)
  1607.         else
  1608.             set_top_line("")
  1609.             printf(SCREEN, "F%d is not an active window", key - F1 + 1)
  1610.             normal_video()
  1611.         end if
  1612.         adding_to_kill = FALSE
  1613.         
  1614.         elsif length(buffer) = 0 and key != ESCAPE then
  1615.         -- empty buffer
  1616.         -- only allowed action is to insert something
  1617.         if key = INSERT or not find(key, CONTROL_CHARS) then
  1618.             -- initialize buffer
  1619.             buffer = {{'\n'}} -- one line with \n
  1620.             b_line = 1
  1621.             b_col = 1
  1622.             s_line = 1
  1623.             s_col = 1
  1624.             if key = INSERT then
  1625.             insert_kill_buffer()
  1626.             else
  1627.             insert(key)
  1628.             end if
  1629.             DisplayLine(1, 1, FALSE)
  1630.         end if
  1631.  
  1632.         elsif key = DELETE then
  1633.         if not adding_to_kill then
  1634.             kill_buffer = {buffer[b_line][b_col]}
  1635.         elsif sequence(kill_buffer[1]) then
  1636.             -- we were building up deleted lines,
  1637.             -- but now we'll switch to chars
  1638.             kill_buffer = {buffer[b_line][b_col]}
  1639.         else
  1640.             kill_buffer = append(kill_buffer, buffer[b_line][b_col])
  1641.         end if
  1642.         delete_char()
  1643.  
  1644.         elsif key = CONTROL_DELETE or key = CONTROL_D then
  1645.         if not adding_to_kill then
  1646.             kill_buffer = {buffer[b_line]}
  1647.         elsif atom(kill_buffer[1]) then
  1648.             -- we were building up deleted chars,
  1649.             -- but now we'll switch to lines
  1650.             kill_buffer = {buffer[b_line]}
  1651.         else
  1652.             kill_buffer = append(kill_buffer, buffer[b_line])
  1653.         end if
  1654.         delete_line(b_line)
  1655.  
  1656.         else
  1657.         if key = PAGE_DOWN then
  1658.             page_down()
  1659.  
  1660.         elsif key = PAGE_UP then
  1661.             page_up()
  1662.  
  1663.         elsif key = ARROW_LEFT then
  1664.             arrow_left()
  1665.  
  1666.         elsif key = ARROW_RIGHT then
  1667.             arrow_right()
  1668.  
  1669.         elsif key = ARROW_DOWN then
  1670.             arrow_down()
  1671.  
  1672.         elsif key = ARROW_UP then
  1673.             arrow_up()
  1674.  
  1675.         elsif key = ' ' then
  1676.             try_auto_complete(key)
  1677.  
  1678.         elsif key = INSERT then
  1679.             insert_kill_buffer()
  1680.  
  1681.         elsif key = BS then
  1682.             arrow_left()
  1683.             delete_char()
  1684.  
  1685.         elsif key = HOME then
  1686.             goto_line(1, 1)
  1687.  
  1688.         elsif key = END then
  1689.             goto_line(length(buffer), 1)
  1690.  
  1691.         elsif key = ESCAPE then
  1692.             -- special command
  1693.             get_escape(FALSE)
  1694.  
  1695.         elsif key = CR then
  1696.             if searching then
  1697.             searching = search(TRUE)
  1698.             normal_video()
  1699.             searching = TRUE -- avoids accidental <CR> insertion
  1700.             else
  1701.             try_auto_complete(key)
  1702.             end if
  1703.         
  1704.         else
  1705.             insert(key)
  1706.  
  1707.         end if
  1708.  
  1709.         adding_to_kill = FALSE
  1710.  
  1711.         end if
  1712.  
  1713.         if key != CR and key != ESCAPE then
  1714.         searching = FALSE
  1715.         end if
  1716.         cursor(ED_CURSOR)
  1717.         set_position(s_line, s_col)
  1718.     
  1719.     else
  1720.         -- illegal key pressed
  1721.         get_escape(TRUE)  -- give him some help
  1722.         set_position(s_line, s_col)
  1723.     end if
  1724.     end while
  1725. end procedure
  1726.  
  1727. procedure ed(sequence command)
  1728. -- editor main procedure 
  1729. -- start editing a new file
  1730. -- ed.ex is executed by ed.bat
  1731. -- command line will be:
  1732. --    ex ed.ex              - get filename from ex.err, or user
  1733. --    ex ed.ex filename     - filename specified
  1734.  
  1735.     file_number file_no
  1736.  
  1737.     start_line = 0
  1738.     start_col = 0
  1739.  
  1740.     if length(command) >= 3 then
  1741.     file_name = lower(command[3])
  1742.     else
  1743.     file_name = get_err_line()
  1744.     end if
  1745.     if length(file_name) = 0 then
  1746.     -- we still don't know the file name - so ask user
  1747.     puts(SCREEN, "file name? ")
  1748.     cursor(ED_CURSOR)
  1749.     file_name = key_gets("")
  1750.     puts(SCREEN, '\n')
  1751.     end if
  1752.     file_name = delete_trailing_white(file_name)
  1753.     if length(file_name) = 0 then
  1754.     abort(1) -- file_name was just whitespace - quit
  1755.     end if
  1756.     file_no = open(file_name, "r")
  1757.  
  1758.     -- turn off multi_color & auto_complete for non .e files
  1759.     multi_color = WANT_COLOR_SYNTAX
  1760.     auto_complete = WANT_AUTO_COMPLETE
  1761.     if not config[VC_COLOR] or config[VC_MODE] = 7 then
  1762.     multi_color = FALSE -- mono monitor
  1763.     end if
  1764.     file_name = file_name & ' '
  1765.     dot_e = FALSE
  1766.     for i = 1 to length(E_FILES) do
  1767.     if match(E_FILES[i] & ' ', file_name) then
  1768.         dot_e = TRUE
  1769.     end if
  1770.     end for
  1771.     if not dot_e then
  1772.     multi_color = FALSE
  1773.     auto_complete = FALSE
  1774.     end if
  1775.     
  1776.     -- use PROG_INDENT tab width for Euphoria & other languages:
  1777.     edit_tab_width = STANDARD_TAB_WIDTH
  1778.     for i = 1 to length(PROG_FILES) do
  1779.        if match(PROG_FILES[i] & ' ', file_name) then
  1780.        edit_tab_width = PROG_INDENT
  1781.        exit
  1782.        end if
  1783.     end for
  1784.     
  1785.     file_name = file_name[1..length(file_name)-1] -- remove ' '
  1786.     if multi_color then
  1787.     init_class()
  1788.     end if
  1789.  
  1790.     adding_to_kill = FALSE
  1791.     clear_modified()
  1792.     buffer_version = 0
  1793.     control_chars = FALSE
  1794.     wrap(0)
  1795.     new_buffer()
  1796.     s_line = 1
  1797.     s_col = 1
  1798.     b_line = 1
  1799.     b_col = 1
  1800.     save_state()
  1801.     if file_no = -1 then
  1802.     set_f_line(window_number, " <new file>")
  1803.     normal_video()
  1804.     ClearWindow()
  1805.     else
  1806.     set_f_line(window_number, "")
  1807.     normal_video()
  1808.     set_position(1, 1)
  1809.     cursor(NO_CURSOR)
  1810.     read_file(file_no)
  1811.     close(file_no)
  1812.     end if
  1813.     set_position(1, 1)
  1814.     edit_file()
  1815. end procedure
  1816.  
  1817. procedure ed_main()
  1818. -- startup and shutdown of ed()
  1819.     sequence cl
  1820.     
  1821.     config = video_config()
  1822.  
  1823.     if config[VC_XPIXELS] > 0 then
  1824.     if graphics_mode(3) then
  1825.     end if
  1826.     config = video_config()
  1827.     end if
  1828.  
  1829.     if config[VC_LINES] != INITIAL_LINES then
  1830.     screen_length = text_rows(INITIAL_LINES)
  1831.     config = video_config()
  1832.     end if
  1833.     screen_length = config[VC_LINES]
  1834.     window_length = screen_length - 1
  1835.  
  1836.     cl = command_line()
  1837.  
  1838.     while length(window_list) > 0 do
  1839.     ed(cl)
  1840.     cl = {"ex", "ed.ex" , file_name}
  1841.     end while
  1842.  
  1843.     -- exit editor
  1844.     if screen_length != FINAL_LINES then
  1845.     screen_length = text_rows(FINAL_LINES)
  1846.     end if
  1847.     cursor(UNDERLINE_CURSOR)
  1848.     bk_color(BLACK)
  1849.     text_color(BLACK)
  1850.     position(screen_length, 1)
  1851.     puts(SCREEN, BLANK_LINE)
  1852.     position(screen_length, 1)
  1853.     text_color(WHITE)
  1854.     puts(SCREEN, " \n")
  1855. end procedure
  1856.  
  1857. ed_main()
  1858. -- This abort statement reduces the chance of 
  1859. -- a syntax error when you edit ed.ex using itself: 
  1860. abort(1) 
  1861.  
  1862.